package com.cdtye.shuoh.service.tgengine.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.cdtye.common.webcore.web.AjaxJson;
import com.cdtye.shuoh.domain.enums.DataTypeEnum;
import com.cdtye.shuoh.domain.tdengine.*;
import com.cdtye.shuoh.mapper.tdengine.TdEngineMapper;
import com.cdtye.shuoh.service.tgengine.TdEngineService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
@DS("tdengine")
public class TdEngineServiceImpl implements TdEngineService {

    /**
     * 数据库名称
     */
    @Value("${spring.datasource.dynamic.datasource.tdengine.dbName:cms_shuoh}")
    String dataBaseName;

    @Autowired
    private TdEngineMapper tdEngineMapper;

    @Override
    public AjaxJson createSuperTable(SuperTableDto superTableDto) throws Exception {
        //获取列字段（超级表结构）对象集合
        List<Fields> schemaFields = superTableDto.getSchemaFields();
        //获取标签字段对象集合
        List<Fields> tagsFields = superTableDto.getTagsFields();
        //取数据库名称
        String dataBaseName = superTableDto.getDataBaseName();
        //获取超级表名称
        String superTableName = superTableDto.getSuperTableName();
        //获取列字段对象集合的第一个对象的字段数据类型
        DataTypeEnum dataType = schemaFields.get(0).getDataType();

        //如果该数据类型不是timestamp，打印和返回报错信息,Tdengine数据库要求第一个字段必须为timestamp
        if (dataType == null || !"timestamp".equals(dataType.getDataType())) {
            log.error("invalid operation: first column must be timestamp");
            return AjaxJson.error("invalid operation: the first column must be timestamp");
        }

        try {
            //将列字段对象集合和标签字段对象集合转码为字段Vo类对象集合
            List<FieldsVo> schemaFieldsVoList = FieldsVo.fieldsTranscoding(schemaFields);
            List<FieldsVo> tagsFieldsVoList = FieldsVo.fieldsTranscoding(tagsFields);
            //创建超级表
            this.tdEngineMapper.createSuperTable(schemaFieldsVoList, tagsFieldsVoList, dataBaseName, superTableName);
            log.info("successful operation: created superTable '" + superTableName + "' success");
            return AjaxJson.success();
        } catch (UncategorizedSQLException e) {
            String message = e.getCause().getMessage();
            try {
                message = message.substring(message.lastIndexOf("invalid operation"));
            } catch (Exception ex) {}
            log.error(message);
            return AjaxJson.error(message);
        } catch (Exception e) {
            log.error(e.getMessage());
            return AjaxJson.error(e.getMessage());
        }
    }


    @Override
    public void createDateBase(String dataBaseName) throws Exception {
        this.tdEngineMapper.createDatabase(dataBaseName);
    }

    @Override
    public AjaxJson createTable(TableDto tableDto) throws Exception {
        try {
            this.tdEngineMapper.createTable(tableDto.getTagsFieldValues(),tableDto.getTableName(),tableDto.getDataBaseName(),tableDto.getSuperTableName());
            log.info("successful operation: create table success");
            return AjaxJson.success("successful operation: create table success");
        }  catch (Exception e) {
            String message = e.getCause().getMessage();
            log.error(message);
            try {
                message = message.substring(message.lastIndexOf("invalid operation"));
            } catch (Exception ex) {
                log.error(ex.getMessage());
            }
            return AjaxJson.error(message);
        }
    }
//
   @Override
    public AjaxJson insertData(TableDto tableDto) throws Exception {
       try {
           List<Fields> tagsFieldValues = tableDto.getTagsFieldValues();
           for (Fields fields : tagsFieldValues) {
               if (StringUtils.isBlank(fields.getFieldName()) || fields.getFieldValue() == null) {
                   log.error("invalid operation: fieldName or fieldValue can not be empty");
                   return AjaxJson.error("invalid operation: fieldName or fieldValue can not be empty");
               }
           }
           this.tdEngineMapper.insertData(tableDto);
           log.info("successful operation: insert data success");
           return AjaxJson.success();
       } catch (UncategorizedSQLException e) {
           String message = e.getCause().getMessage();
           try {
               message = message.substring(message.lastIndexOf("invalid operation"));
           } catch (Exception ex) {}
           log.error(message);
           return AjaxJson.error(message);
       } catch (Exception e) {
           log.error(e.getMessage());
           return AjaxJson.error(e.getMessage());
       }
    }

    /**
     * @param selectDto
     * @return
     */
    @Override
    public List<Map<String, Object>> getLastData(SelectDto selectDto) throws Exception {
        List<Map<String, Object>> maps = this.tdEngineMapper.getLastData(selectDto);
        return maps;
    }

    @Override
    public List<Map<String, Object>> getLastNumData(SelectDto selectDto) throws Exception {
        List<Map<String, Object>> maps = this.tdEngineMapper.getLastNumData(selectDto);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(null!=maps && maps.size()>0){
            //Collections.reverse(maps);
            maps.sort(Comparator.comparing((Map<String, Object> h) -> (sdf.format( h.get("event_time")))));
        }
        return maps;
    }


    /**
     * @param tagsSelectDao
     * @return
     */
    @Override
    public Map<String, Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao) {
        List<Map<String, Object>> maps = this.tdEngineMapper.getLastDataByTags(tagsSelectDao);
        Map<String, Map<String, Object>> objectHashMap = new HashMap<>();
        for (Map<String, Object> map : maps) {
            objectHashMap.put(map.get(tagsSelectDao.getTagsName()).toString(), map);
        }
        return objectHashMap;
    }


    @Override
    public List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto) {
        List<Map<String, Object>> maps = this.tdEngineMapper.getHistoryData(selectVisualDto);
        if(null!=maps && maps.size()>0){
            Collections.reverse(maps);
        }
        return maps;
    }

    @Override
    public List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto) {
        List<Map<String, Object>> maps = this.tdEngineMapper.getRealtimeData(selectVisualDto);
        if(null!=maps && maps.size()>0){
            Collections.reverse(maps);
        }
        return maps;
    }

    @Override
    public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) {
        List<Map<String, Object>> maps = this.tdEngineMapper.getAggregateData(selectVisualDto);
        if(null!=maps && maps.size()>0){
            Collections.reverse(maps);
        }
        return maps;
    }

    //avg/max/sum
    @Override
    public Map<String,Object> getAllAggregateData(SelectVisualDto selectVisualDto){
        selectVisualDto.setAggregate("avg");
        List<Map<String, Object>> avgMaps = this.getAggregateData(selectVisualDto);
        selectVisualDto.setAggregate("max");
        List<Map<String, Object>> maxMaps = this.getAggregateData(selectVisualDto);
        selectVisualDto.setAggregate("min");
        List<Map<String, Object>> minMaps = this.getAggregateData(selectVisualDto);
        Map<String,Object> result = new HashMap<>();
        if(null == avgMaps) return result;
        List<Object> time = new ArrayList<>();
        List<Object> avg = new ArrayList<>();
        List<Object> max = new ArrayList<>();
        List<Object> min = new ArrayList<>();
        for(int i=0;i<avgMaps.size();i++){
            time.add(avgMaps.get(i).get("ts"));
            avg.add(avgMaps.get(i).get("name"));
            max.add(maxMaps.get(i).get("name"));
            min.add(minMaps.get(i).get("name"));
        }
        result.put("time",time);
        result.put("avg",avg);
        result.put("max",max);
        result.put("min",min);
        return result;
    }


}
